package com.example.boot.service.transaction.impl;

import com.example.boot.domain.User;
import com.example.boot.mapper.simple_test.UserMapper;
import com.example.boot.service.transaction.ITransactionRequiredService;
import com.example.boot.service.transaction.ITransactionService1;
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.annotation.Resource;

/**
 * 在springAOP的用法中，只有代理的类才会被切入，我们在controller层调用service的方法的时候，
 * 是可以被切入的，但是如果我们在service层 A方法中，调用B方法，切点切的是B方法，那么这时候是不会切入的
 */

@Service
public class TransactionRequiredServiceImpl implements ITransactionRequiredService {

    @Resource
    private UserMapper userMapper;
    @Resource
    private ITransactionService1 iTransactionService1;

    /**
     * 1.1 场景一
     * 此场景外围方法没有开启事务。
     * 验证方法1：
     * “张三”、“李四”均插入。
     */
    @Override
    public void notransaction_exception_required_required() {
        User user1=new User();
        user1.setName("张三");
        addRequired1(user1);

        User user2=new User();
        user2.setName("李四");
        addRequired2(user2);

        throw new RuntimeException();
    }

    /**
     * 1.1 场景一
     * 此场景外围方法没有开启事务。
     * 验证方法2：
     */
    @Override
    public void notransaction_required_required_exception() {
        User user1=new User();
        user1.setName("张三");
        this.addRequired1(user1);

        User user2=new User();
        user2.setName("李四");
        // 此处事务不生效,此方法即使报错，张三，李四均插入
        // this.addRequiredException(user2);

        // 生效,此方法即使报错，张三插入，李四不插入
        // ((ITransactionService)AopContext.currentProxy()).addRequiredException(user2);
        // ITransactionService cast = ITransactionService.class.cast(AopContext.currentProxy());
        // cast.addRequiredException(user2);

        // 生效,此方法即使报错，张三插入，李四不插入
        iTransactionService1.addRequiredException(user2);
    }

    /**
     * 1.2 场景二
     * 外围方法开启事务，这个是使用率比较高的场景。
     * 张三李四均未插入
     * 验证方法1：
     */
    @Override
    @Transactional(propagation  = Propagation.REQUIRED)
    public void transaction_exception_required_required(){
        User user1=new User();
        user1.setName("张三");
        addRequired1(user1);

        User user2=new User();
        user2.setName("李四");
        addRequired2(user2);

        throw new RuntimeException();
    }


    /**
     * 1.2 场景二
     * 外围方法开启事务，这个是使用率比较高的场景。
     * 张三李四均未插入
     * 验证方法2：
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void transaction_required_required_exception(){
        User user1=new User();
        user1.setName("张三");
        addRequired1(user1);

        User user2=new User();
        user2.setName("李四");
        addRequiredException(user2);
    }

    /**
     * 1.2 场景二
     * 外围方法开启事务，这个是使用率比较高的场景。
     * 张三李四均未插入
     * 验证方法3：
     * 外围方法开启事务，内部方法加入外围方法事务，内部方法抛出异常回滚，即使方法被catch不被外围方法感知，整个事务依然回滚。
     */
    @Transactional
    @Override
    public void transaction_required_required_exception_try(){
        User user1=new User();
        user1.setName("张三");
        addRequired1(user1);

        User user2=new User();
        user2.setName("李四");
        try {
            // 事务不生效，张三李四均插入
            // this.addRequiredException(user2);

            // 事务生效，张三李四均能回滚，会报错：Transaction rolled back because it has been marked as rollback-only
            ((ITransactionRequiredService) AopContext.currentProxy()).addRequiredException(user2);
            // ITransactionService cast = ITransactionService.class.cast(AopContext.currentProxy());
            // cast.addRequiredException(user2);

            // 事务生效，张三李四均能回滚，会报错：Transaction rolled back because it has been marked as rollback-only，
            // 在catch中加上TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();可使其不报错且正常回滚
            // iTransactionService1.addRequiredException(user2);
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            System.out.println("方法回滚");
        }
    }


    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addRequired1(User user1){
        userMapper.insert(user1);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addRequired2(User user2){
        userMapper.insert(user2);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addRequiredException(User user2){
        userMapper.insert(user2);
        throw new RuntimeException();
    }
}
